knitr::opts_chunk$set(echo = T, 
                      warning = F, 
                      message = F, 
                      include = T,
                      fig.width = 10,
                      fig.height = 5)
library(tidyverse)
library(RSocrata)
library(RcppRoll)
library(ggiteam)
#library(staplr)
library(scales)
library(lubridate)
library(rgdal)


library(leaflet)
library(RODBC)
library(htmltools)
library(readxl)
#library(leaflet.extras)
#library(mapview)

source("../src/functions.R")
microzones <- readOGR("../data/raw/Micro_Zones", "Zones")
OGR data source with driver: ESRI Shapefile 
Source: "/Users/justin/Google Drive/work_analysis/policestat/policestat_reporting/data/raw/Micro_Zones", layer: "Zones"
with 131 features
It has 13 fields
Integer64 fields read as strings:  OID_ ORIG_FID 
#street.guncounts <- readOGR("../data/Streets_GunCounts", "Streets_GunCounts")
vri <- readOGR("../data/raw/VRI_Zones_2019", "VRI_Zones_Jan2019", verbose = T)
OGR data source with driver: ESRI Shapefile 
Source: "/Users/justin/Google Drive/work_analysis/policestat/policestat_reporting/data/raw/VRI_Zones_2019", layer: "VRI_Zones_Jan2019"
with 8 features
It has 5 fields
Integer64 fields read as strings:  OBJECTID Id 
posts <- readOGR("../data/raw/Police_Post_Shapefile", "Posts_As_of_1_22_2019",
                 verbose = T)
OGR data source with driver: ESRI Shapefile 
Source: "/Users/justin/Google Drive/work_analysis/policestat/policestat_reporting/data/raw/Police_Post_Shapefile", layer: "Posts_As_of_1_22_2019"
with 141 features
It has 21 fields
Integer64 fields read as strings:  OBJECTID_1 OBJECTID_2 OBJECTID Count_ 
districts <- readOGR("../data/raw/Districts", "Police_Districts",
                     verbose = T)
OGR data source with driver: ESRI Shapefile 
Source: "/Users/justin/Google Drive/work_analysis/policestat/policestat_reporting/data/raw/Districts", layer: "Police_Districts"
with 9 features
It has 9 fields
Integer64 fields read as strings:  OBJECTID_1 OBJECTID 
  

query <- paste0("https://data.baltimorecity.gov/resource/wsfq-mvij.json?$where=",
                "(Description like 'HOMICIDE' OR ",
                "Description like 'SHOOTING' OR ", 
                "Description like 'RAPE' OR ", 
                "Description like 'AGG. ASSAULT' OR ",
                "contains(Description, 'ROBBERY'))")

df <- read.socrata(query)
microzones <- spTransform(microzones, CRS("+init=epsg:4326"))
vri <- spTransform(vri, CRS("+init=epsg:4326"))
posts <- spTransform(posts, CRS("+init=epsg:4326"))
df <- df %>% 
  filter(!is.na(latitude),
         year(crimedate) >= 2017) %>%
  mutate(crimedate = as.Date(crimedate),
         longitude = as.numeric(longitude),
         latitude = as.numeric(latitude),
         description = ifelse(grepl("ROBBERY", description), 
                              "ROBBERY", description),
         description_grouped = 
           case_when(
             grepl("ROBBERY", description) & grepl("FIREARM", weapon) ~ "ARMED ROBBERY",
             grepl("ROBBERY", description) & !grepl("FIREARM", weapon) ~ "UNARMED ROBBERY",
             description %in% c("HOMICIDE", "SHOOTING") ~ "HOMICIDE/SHOOTING",
             TRUE ~ description)
  )

last_date <- max(df$crimedate)
# convert crime df to geospatial
df_geo <- SpatialPointsDataFrame(
  coords = df %>% select(longitude, latitude), 
  df, 
  proj4string = CRS("+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0")
)

# transform to right coords system
df_geo <- spTransform(
  df_geo, 
  CRSobj = CRS("+init=epsg:4326 +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84
+towgs84=0,0,0")
)

# tag each crime with the post
df_tagged_post <- over(df_geo, posts) 
df_tagged_microzones <- over(df_geo, microzones) 

# join the tag info back to the crime geospatial df
df_geo@data <- bind_cols(df_geo@data, df_tagged_post) %>%
  rename("post_correct" = Area) %>%
  bind_cols(df_tagged_microzones)
# get counts of shootings by post since 2018
post_shootings_2018_to_present <- df_geo@data %>% 
  filter(year(crimedate) >= 2018, 
         description == "SHOOTING") %>% 
  count(post_correct) %>%
  rename("shootings_since_2018" = n)

# join the counts to the post shapefile
posts@data <- posts@data %>% 
  left_join(
    post_shootings_2018_to_present,
    by = c("Area" = "post_correct")
  )

microzone_shootings_2018_to_present <- df_geo@data %>%
  filter(year(crimedate) >= 2018,
         description == "SHOOTING") %>%
  count(HS_Zone) %>%
  rename("shootings_since_2018" = n)

microzones@data <- microzones@data %>%
  left_join(
    microzone_shootings_2018_to_present,
    by = c("HS_Zone" = "HS_Zone")
  )

# By district
counts_districts <- df_geo@data %>%
  count(description, district, crimedate) %>%
  group_by(description, district) %>%
  complete(crimedate = seq.Date(as.Date(min(df_geo@data$crimedate)), 
                                as.Date(max(df_geo@data$crimedate)),
                                by="day")) %>%
  replace_na(list(n = 0)) %>%
  ungroup()

hom_shot_combo_district_counts <- counts_districts %>% 
  filter(description %in% c("SHOOTING", "HOMICIDE")) %>% 
  group_by(district, crimedate) %>% 
  select(description, district, crimedate, n) %>% 
  spread(key = description, value = n) %>% 
  mutate(n = HOMICIDE + SHOOTING, 
         description = "HOMICIDE + SHOOTING") %>% 
  select(-HOMICIDE, -SHOOTING) %>% 
  ungroup()

rolling_counts_districts <- counts_districts %>%
  bind_rows(hom_shot_combo_district_counts) %>%
  arrange(crimedate) %>%
  group_by(district, description) %>%
  mutate(roll_28 = roll_sum(x = n, n = 28, align = "right", fill = NA),
         roll_7 = roll_sum(x = n, n = 7, align = "right", fill = NA),
         roll_90 = roll_sum(x = n, n = 90, align = "right", fill = NA)) %>%
  ungroup() %>%
  arrange(description, district, crimedate)

# Citywide

counts <- df_geo@data %>%
  count(description, crimedate) %>%
  group_by(description) %>%
  complete(crimedate = seq.Date(as.Date(min(df_geo@data$crimedate)), 
                                as.Date(max(df_geo@data$crimedate)),
                                by="day")) %>%
  replace_na(list(n = 0)) %>%
  mutate(district = "CITYWIDE")


hom_shot_combo_counts <- counts %>% 
  filter(description %in% c("SHOOTING", "HOMICIDE")) %>% 
  group_by(crimedate) %>% 
  select(description, crimedate, n) %>% 
  spread(key = description, value = n) %>% 
  mutate(n = HOMICIDE + SHOOTING, 
         description = "HOMICIDE + SHOOTING",
         district = "CITYWIDE") %>% 
  select(-HOMICIDE, -SHOOTING) %>% 
  ungroup()

rolling_counts <- counts %>%
  bind_rows(hom_shot_combo_counts) %>%
  arrange(crimedate) %>%
  group_by(description) %>%
  mutate(roll_28 = roll_sum(x = n, n = 28, align = "right", fill = NA),
         roll_7 = roll_sum(x = n, n = 7, align = "right", fill = NA),
         roll_90 = roll_sum(x = n, n = 90, align = "right", fill = NA)) %>%
  ungroup() %>%
  arrange(description, crimedate)

Citywide


# folder <- paste0("../output/plots/", last_date, "/")
folder <- paste0("../../../../../Google Drive/Office of Performance & Innovation/CitiStat/PoliceStat/Regular Reporting/", last_date, "/")
 
if (!dir.exists(folder)){
  dir.create(folder)
}


for(desc in unique(rolling_counts$description)){
  
  fn_roll90 <- paste0(folder, last_date, "_rolling_90_day_citywide_", desc, ".png")
  fn_roll28 <- paste0(folder, last_date, "_rolling_28_day_citywide_", desc, ".png")
  
  plt_roll28 <- roll28_district_facet_plot(rolling_counts, desc)
  plt_roll90 <- roll90_district_facet_plot(rolling_counts, desc)
  
  ggsave(filename = fn_roll28, plt_roll28, device = "png", 
         width = 5, height = 3, units = "in")
  
  ggsave(filename = fn_roll90, plt_roll90, device = "png", 
         width = 5, height = 3, units = "in")
  
  
  print(plt_roll28)
  print(plt_roll90)
}

roll90_district_facet_plot(rolling_counts, "HOMICIDE + SHOOTING") +
  geom_text(aes(x = as.Date("2019-07-01") - 14 , y = 250, label = "July 1"),
            color = "red", family = "oswald", hjust = 1) +
  theme_iteam_presentations()

roll90_district_facet_plot_sparkline(rolling_counts, "HOMICIDE + SHOOTING") +
  # geom_text(aes(x = as.Date("2019-07-01") - 14 , y = 250, label = "July 1"),
  #           color = "red", family = "oswald", hjust = 1) +
  #theme_iteam_presentations() +
  theme(panel.grid.major.x = element_line(size=.5, color="gray90" ),
        panel.grid.major.y = element_blank(),
        title = element_blank(),
        axis.title.y = element_blank())

NA

By District




for(desc in unique(rolling_counts_districts$description)){
  
  fn_roll90 <- paste0(folder, last_date, "_rolling_90_day_by_district_", desc, ".png")
  fn_roll28 <- paste0(folder, last_date, "_rolling_28_day_by_district_", desc, ".png")
  
  plt_roll28 <- roll28_district_facet_plot(rolling_counts_districts, desc)
  plt_roll90 <- roll90_district_facet_plot(rolling_counts_districts, desc)
  
  ggsave(filename = fn_roll28, plt_roll28, device = "png", 
         width = 7, height = 9, units = "in")
  
  ggsave(filename = fn_roll90, plt_roll90, device = "png", 
         width = 7, height = 9, units = "in")
  
  
  print(plt_roll28)
  print(plt_roll90)
}


#output.png <- paste0(folder, last_date, "_rolling_28_day_by_district.png")
#staple_png(input_directory = folder, output_filepath = "output.png") not working
roll90_single_district_plot("HOMICIDE + SHOOTING", "CITY-WIDE") +
  geom_vline(aes(xintercept = as.Date("2019-07-01")), color = "red", size = .5, linetype = "dotted") +
  theme_iteam_presentations()
no non-missing arguments to max; returning -Infno non-missing arguments to min; returning Infno non-missing arguments to max; returning -InfError: Faceting variables must have at least one value

hom_shot_cumsums <- df_geo@data %>%
  filter(description %in% c("HOMICIDE", "SHOOTING")) %>%
  count(crimedate) %>%
  complete(crimedate = seq.Date(as.Date(min(df_geo@data$crimedate)), 
                                as.Date(max(df_geo@data$crimedate)),
                                by="day")) %>%
  replace_na(list(n = 0)) %>%
  arrange(crimedate) %>%
  mutate(day_of_year = as.numeric(strftime(crimedate, "%j")),
         crime.year = year(crimedate)) %>%
  group_by(crime.year) %>%
  mutate(hs_cumsum = cumsum(n))

hom_shot_current <- hom_shot_cumsums %>%
  filter(crime.year == 2019) %>%
  summarise(max(hs_cumsum)) %>%
  pull()

current_day <- hom_shot_cumsums %>%
  filter(!is.na(hs_cumsum),
         crime.year == 2019) %>%
  ungroup() %>%
  summarise(max(day_of_year)) %>%
  pull()

hom_shot_projections <- round(365 * hom_shot_current / current_day, 0)

cum.plot <- hom_shot_cumsums %>%
  ggplot(aes(day_of_year, hs_cumsum, 
             group = crime.year, 
             color = as.factor(crime.year))) +
  geom_line() +
  #scale_alpha_manual(values = as.factor(c(1, 1, 0.3, 1., 1))) +
  theme_iteam_presentations() +
  scale_color_discrete_iteam() +
  theme(legend.title = element_blank()) +
  ylab("Cumulative\nHom. + Shots.") +
  xlab("Day of Year") 

ggsave(filename = paste0(folder, last_date, "cumulative_hom_shoot.png"), cum.plot, device = "png", 
       width = 4, height = 2, units = "in")

cum.plot

hom_cumsums <- df_geo@data %>%
  filter(description %in% c("HOMICIDE")) %>%
  count(crimedate) %>%
  complete(crimedate = seq.Date(as.Date(min(df_geo@data$crimedate)), 
                                as.Date(max(df_geo@data$crimedate)),
                                by="day")) %>%
  replace_na(list(n = 0)) %>%
  arrange(crimedate) %>%
  mutate(day_of_year = as.numeric(strftime(crimedate, "%j")),
         crime.year = year(crimedate)) %>%
  group_by(crime.year) %>%
  mutate(hom_cumsum = cumsum(n))

hom_current <- hom_cumsums %>%
  filter(crime.year == 2019) %>%
  summarise(max(hom_cumsum)) %>%
  pull()

hom_projections <- round(365 * hom_current / current_day, 0)

cum.plot <- hom_cumsums %>%
  ggplot(aes(day_of_year, hom_cumsum, 
             group = crime.year, 
             color = as.factor(crime.year))) +
  geom_line() +
  geom_point(aes(x = hom_cumsums %>% 
                   filter(crime.year == 2019) %>%
                   summarise(max(day_of_year)) %>%
                   pull(), 
                 y = hom_cumsums %>% 
                   filter(crime.year == 2019) %>%
                   summarise(max(hom_cumsum)) %>%
                   pull()),
             color = iteam.colors[3]) +
  geom_point(aes(x = 365, 
                 y = hom_cumsums %>% 
                   filter(crime.year == 2018) %>%
                   summarise(max(hom_cumsum)) %>%
                   pull()),
             color = iteam.colors[2]) +
  geom_point(aes(x = 365, 
                 y = hom_cumsums %>% 
                   filter(crime.year == 2017) %>%
                   summarise(max(hom_cumsum)) %>%
                   pull()),
             color = iteam.colors[1]) +
  geom_point(aes(x = 365, y = hom_projections), color = iteam.colors[3]) +
  
  geom_text(aes(x = hom_cumsums %>% 
                  filter(crime.year == 2019) %>%
                  summarise(max(day_of_year)) %>%
                  pull(), 
                y = hom_cumsums %>% 
                  filter(crime.year == 2019) %>%
                  summarise(max(hom_cumsum)) %>%
                  pull() +10,
                label = hom_cumsums %>% 
                  filter(crime.year == 2019) %>%
                  summarise(max(hom_cumsum)) %>%
                  pull(),
                hjust = 1),
            color = iteam.colors[3]) +
  geom_text(aes(x = 375, 
                y = hom_cumsums %>% 
                  filter(crime.year == 2018) %>%
                  summarise(max(hom_cumsum)) %>%
                  pull(),
                label = hom_cumsums %>% 
                  filter(crime.year == 2018) %>%
                  summarise(max(hom_cumsum)) %>%
                  pull(),
                hjust = 0),
            color = iteam.colors[2]) +
  geom_text(aes(x = 375, 
                y = hom_cumsums %>% 
                  filter(crime.year == 2017) %>%
                  summarise(max(hom_cumsum)) %>%
                  pull() - 10,
                label = hom_cumsums %>% 
                  filter(crime.year == 2017) %>%
                  summarise(max(hom_cumsum)) %>%
                  pull(),
                hjust = 0),
            color = iteam.colors[1]) +
  geom_text(aes(x = 375, y = hom_projections + 10,
                label = paste(hom_projections, "(proj.)"),
                hjust = 0), 
                color = iteam.colors[3]) +
  #scale_alpha_manual(values = as.factor(c(1, 1, 0.3, 1., 1))) +
  theme_iteam_presentations() +
  scale_color_discrete_iteam() +
  theme(legend.title = element_blank()) +
  scale_x_continuous(breaks = c(0, 100, 200, 300, 365),
                     limits = c(0, 450)) +
  ylab("Cumulative Homicides") +
  xlab("Day of Year") 

#ggsave(filename = paste0(folder, last_date, "cumulative_hom_shoot.png"), cum.plot, device = "png", 
#       width = 4, height = 2, units = "in")

cum.plot

df %>%
  filter(description %in% c("HOMICIDE", "SHOOTING"),
         year(crimedate) %in% c(2018, 2019)) %>%
  count(crimedate) %>%
  group_by(year(crimedate)) %>%
  arrange(crimedate) %>%
  mutate(day_of_year = row_number(),
         hs_cumsum = cumsum(n)) %>%
  filter(month(crimedate) == 8)
cum.plot <- df_geo@data %>%
  filter(description %in% c("HOMICIDE", "SHOOTING"),
         year(crimedate) == 2019) %>%
  complete(crimedate = seq.Date(as.Date(min(df_geo@data$crimedate)), 
                                as.Date(max(df_geo@data$crimedate)),
                                by="day")) %>%
  count(description, crimedate) %>%
  replace_na(list(n = 0)) %>%
  arrange(crimedate) %>%
  mutate(day_of_year = as.numeric(strftime(crimedate, "%j"))) %>%
  group_by(description) %>%
  mutate(hs_cumsum = cumsum(n),
         hs_cumpct = hs_cumsum / sum(n)) %>%
  ggplot(aes(day_of_year, hs_cumpct, 
             group = description, 
             color = as.factor(description))) +
  geom_line() +
  #scale_alpha_manual(values = as.factor(c(1, 1, 0.3, 1., 1))) +
  theme_iteam_presentations() +
  scale_color_discrete_iteam() +
  theme(legend.title = element_blank()) +
  ylab("Cum. Hom. + Shots.") +
  xlab("Day of Year") +
  xlim(c(0,250))

#ggsave(filename = paste0(folder, last_date, "cumulative_hom_shoot.png"), cum.plot, device = "png", 
#       width = 4, height = 2, units = "in")

cum.plot

counts %>%
  filter(description %in% c("HOMICIDE", "SHOOTING"),
         year(crimedate) == 2019) %>%
  group_by(description) %>%
  mutate(hs_cumsum = cumsum(n),
         day_of_year = as.numeric(strftime(crimedate, "%j")),
         day.avg = hs_cumsum / day_of_year) %>%
  ggplot(aes(day_of_year, day.avg)) +
  geom_line(aes(color = description)) 

counts %>%
  filter(description %in% c("HOMICIDE", "SHOOTING"),
         year(crimedate) == 2019) %>%
  group_by(description) %>%
  mutate(hs_cumsum = cumsum(n),
         day_of_year = as.numeric(strftime(crimedate, "%j")),
         day.avg = hs_cumsum / day_of_year) %>%
  select(-crimedate, -n, -day.avg, - district) %>%
  spread(description, hs_cumsum) %>%
  mutate(fatality = HOMICIDE / (HOMICIDE + SHOOTING)) %>%
  ggplot(aes(day_of_year, fatality)) + 
  geom_line()

roll90_single_district_plot("ROBBERY", "NORTHEAST")

dec_the_inc <- df_geo@data %>%
  filter(description_grouped == "HOMICIDE/SHOOTING") %>%
  count(crimedate) %>%
  complete(crimedate = seq.Date(as.Date(min(df_geo@data$crimedate)), 
                                as.Date(max(df_geo@data$crimedate)),
                                by="day")) %>%
  replace_na(list(n = 0)) %>%
  arrange(crimedate) %>%
  mutate(day_of_year = as.numeric(strftime(crimedate, "%j")),
         crime.year = year(crimedate)) %>%
  filter(crime.year %in% c(2018, 2019)) %>%
  group_by(crime.year) %>%
  mutate(hs_cumsum = cumsum(n)) %>%
  select(-n, -crimedate) %>%
  spread(key = crime.year, value = hs_cumsum) %>%
  mutate(pct_change = (`2019` - `2018`) / `2018`)
  
dec_the_inc %>%
  ggplot(aes(day_of_year, pct_change)) +
  geom_line() +
  geom_point(data = subset(dec_the_inc, day_of_year == yday(last_date)), 
             aes(x = day_of_year, y = pct_change),
             color = "red", size =2) +
  geom_point(data = subset(dec_the_inc, day_of_year == yday(last_date) - 28), 
             aes(x = day_of_year, y = pct_change),
             color = "red", size =2) +
    geom_point(data = subset(dec_the_inc, day_of_year == yday("2019-07-01")), 
             aes(x = day_of_year, y = pct_change),
             color = "red", size =2) +
  theme_iteam_presentations() +
  scale_color_discrete_iteam() +
  theme(legend.title = element_blank()) +
  ylab("% Change from 2018") +
  xlab("Day of Year")  +
  scale_y_continuous(limits = c(-.5, .5), labels = scales::percent) 

  
# ggsave(filename = paste0(folder, last_date, "cumulative_hom_shoot.png"), cum.plot, device = "png", 
#        width = 4, height = 2, units = "in")

dec_the_inc
df_geo@data %>%
  count(year(crimedate), description)
NA
roll90_single_district_plot("HOMICIDE + SHOOTING", "SOUTHWEST")

LS0tCnRpdGxlOiAiUG9saWNlU3RhdCBCaXdlZWtseSBSZXBvcnRpbmciCmF1dGhvcjogIkp1c3RpbiBFbHN6YXN6IgplbWFpbDogImp1c3Rpbi5lbHN6YXN6QGJhbHRpbW9yZWNpdHkuZ292IgpkYXRlOiAiVHVlc2RheSwgSnVseSA5LCAyMDE5IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGZpZ19oZWlnaHQ6IDQKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDIKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IFQsIGVjaG8gPSBULCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlID0gVFJVRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBULCAKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUgPSBULAogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gMTAsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0ID0gNSkKYGBgCgpgYGB7ciBsaWJyYXJpZXN9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KFJTb2NyYXRhKQpsaWJyYXJ5KFJjcHBSb2xsKQpsaWJyYXJ5KGdnaXRlYW0pCiNsaWJyYXJ5KHN0YXBscikKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KHJnZGFsKQoKCmxpYnJhcnkobGVhZmxldCkKbGlicmFyeShST0RCQykKbGlicmFyeShodG1sdG9vbHMpCmxpYnJhcnkocmVhZHhsKQojbGlicmFyeShsZWFmbGV0LmV4dHJhcykKI2xpYnJhcnkobWFwdmlldykKCnNvdXJjZSgiLi4vc3JjL2Z1bmN0aW9ucy5SIikKYGBgCgpgYGB7ciBsb2FkX2RhdGF9Cm1pY3Jvem9uZXMgPC0gcmVhZE9HUigiLi4vZGF0YS9yYXcvTWljcm9fWm9uZXMiLCAiWm9uZXMiKQojc3RyZWV0Lmd1bmNvdW50cyA8LSByZWFkT0dSKCIuLi9kYXRhL1N0cmVldHNfR3VuQ291bnRzIiwgIlN0cmVldHNfR3VuQ291bnRzIikKdnJpIDwtIHJlYWRPR1IoIi4uL2RhdGEvcmF3L1ZSSV9ab25lc18yMDE5IiwgIlZSSV9ab25lc19KYW4yMDE5IiwgdmVyYm9zZSA9IFQpCnBvc3RzIDwtIHJlYWRPR1IoIi4uL2RhdGEvcmF3L1BvbGljZV9Qb3N0X1NoYXBlZmlsZSIsICJQb3N0c19Bc19vZl8xXzIyXzIwMTkiLAogICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBUKQpkaXN0cmljdHMgPC0gcmVhZE9HUigiLi4vZGF0YS9yYXcvRGlzdHJpY3RzIiwgIlBvbGljZV9EaXN0cmljdHMiLAogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVCkKCgogIAoKcXVlcnkgPC0gcGFzdGUwKCJodHRwczovL2RhdGEuYmFsdGltb3JlY2l0eS5nb3YvcmVzb3VyY2Uvd3NmcS1tdmlqLmpzb24/JHdoZXJlPSIsCiAgICAgICAgICAgICAgICAiKERlc2NyaXB0aW9uIGxpa2UgJ0hPTUlDSURFJyBPUiAiLAogICAgICAgICAgICAgICAgIkRlc2NyaXB0aW9uIGxpa2UgJ1NIT09USU5HJyBPUiAiLCAKICAgICAgICAgICAgICAgICJEZXNjcmlwdGlvbiBsaWtlICdSQVBFJyBPUiAiLCAKICAgICAgICAgICAgICAgICJEZXNjcmlwdGlvbiBsaWtlICdBR0cuIEFTU0FVTFQnIE9SICIsCiAgICAgICAgICAgICAgICAiY29udGFpbnMoRGVzY3JpcHRpb24sICdST0JCRVJZJykpIikKCmRmIDwtIHJlYWQuc29jcmF0YShxdWVyeSkKYGBgCgoKYGBge3IgZ2VvX3RyYW5zZm9ybX0KbWljcm96b25lcyA8LSBzcFRyYW5zZm9ybShtaWNyb3pvbmVzLCBDUlMoIitpbml0PWVwc2c6NDMyNiIpKQp2cmkgPC0gc3BUcmFuc2Zvcm0odnJpLCBDUlMoIitpbml0PWVwc2c6NDMyNiIpKQpwb3N0cyA8LSBzcFRyYW5zZm9ybShwb3N0cywgQ1JTKCIraW5pdD1lcHNnOjQzMjYiKSkKYGBgCgpgYGB7cn0KZGYgPC0gZGYgJT4lIAogIGZpbHRlcighaXMubmEobGF0aXR1ZGUpLAogICAgICAgICB5ZWFyKGNyaW1lZGF0ZSkgPj0gMjAxNykgJT4lCiAgbXV0YXRlKGNyaW1lZGF0ZSA9IGFzLkRhdGUoY3JpbWVkYXRlKSwKICAgICAgICAgbG9uZ2l0dWRlID0gYXMubnVtZXJpYyhsb25naXR1ZGUpLAogICAgICAgICBsYXRpdHVkZSA9IGFzLm51bWVyaWMobGF0aXR1ZGUpLAogICAgICAgICBkZXNjcmlwdGlvbiA9IGlmZWxzZShncmVwbCgiUk9CQkVSWSIsIGRlc2NyaXB0aW9uKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJST0JCRVJZIiwgZGVzY3JpcHRpb24pLAogICAgICAgICBkZXNjcmlwdGlvbl9ncm91cGVkID0gCiAgICAgICAgICAgY2FzZV93aGVuKAogICAgICAgICAgICAgZ3JlcGwoIlJPQkJFUlkiLCBkZXNjcmlwdGlvbikgJiBncmVwbCgiRklSRUFSTSIsIHdlYXBvbikgfiAiQVJNRUQgUk9CQkVSWSIsCiAgICAgICAgICAgICBncmVwbCgiUk9CQkVSWSIsIGRlc2NyaXB0aW9uKSAmICFncmVwbCgiRklSRUFSTSIsIHdlYXBvbikgfiAiVU5BUk1FRCBST0JCRVJZIiwKICAgICAgICAgICAgIGRlc2NyaXB0aW9uICVpbiUgYygiSE9NSUNJREUiLCAiU0hPT1RJTkciKSB+ICJIT01JQ0lERS9TSE9PVElORyIsCiAgICAgICAgICAgICBUUlVFIH4gZGVzY3JpcHRpb24pCiAgKQoKbGFzdF9kYXRlIDwtIG1heChkZiRjcmltZWRhdGUpCgoKYGBgCgoKYGBge3J9CiMgY29udmVydCBjcmltZSBkZiB0byBnZW9zcGF0aWFsCmRmX2dlbyA8LSBTcGF0aWFsUG9pbnRzRGF0YUZyYW1lKAogIGNvb3JkcyA9IGRmICU+JSBzZWxlY3QobG9uZ2l0dWRlLCBsYXRpdHVkZSksIAogIGRmLCAKICBwcm9qNHN0cmluZyA9IENSUygiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQgK25vX2RlZnMgK2VsbHBzPVdHUzg0ICt0b3dnczg0PTAsMCwwIikKKQoKIyB0cmFuc2Zvcm0gdG8gcmlnaHQgY29vcmRzIHN5c3RlbQpkZl9nZW8gPC0gc3BUcmFuc2Zvcm0oCiAgZGZfZ2VvLCAKICBDUlNvYmogPSBDUlMoIitpbml0PWVwc2c6NDMyNiArcHJvaj1sb25nbGF0ICtkYXR1bT1XR1M4NCArbm9fZGVmcyArZWxscHM9V0dTODQKK3Rvd2dzODQ9MCwwLDAiKQopCgojIHRhZyBlYWNoIGNyaW1lIHdpdGggdGhlIHBvc3QKZGZfdGFnZ2VkX3Bvc3QgPC0gb3ZlcihkZl9nZW8sIHBvc3RzKSAKZGZfdGFnZ2VkX21pY3Jvem9uZXMgPC0gb3ZlcihkZl9nZW8sIG1pY3Jvem9uZXMpIAoKIyBqb2luIHRoZSB0YWcgaW5mbyBiYWNrIHRvIHRoZSBjcmltZSBnZW9zcGF0aWFsIGRmCmRmX2dlb0BkYXRhIDwtIGJpbmRfY29scyhkZl9nZW9AZGF0YSwgZGZfdGFnZ2VkX3Bvc3QpICU+JQogIHJlbmFtZSgicG9zdF9jb3JyZWN0IiA9IEFyZWEpICU+JQogIGJpbmRfY29scyhkZl90YWdnZWRfbWljcm96b25lcykKCgpgYGAKCmBgYHtyfQojIGdldCBjb3VudHMgb2Ygc2hvb3RpbmdzIGJ5IHBvc3Qgc2luY2UgMjAxOApwb3N0X3Nob290aW5nc18yMDE4X3RvX3ByZXNlbnQgPC0gZGZfZ2VvQGRhdGEgJT4lIAogIGZpbHRlcih5ZWFyKGNyaW1lZGF0ZSkgPj0gMjAxOCwgCiAgICAgICAgIGRlc2NyaXB0aW9uID09ICJTSE9PVElORyIpICU+JSAKICBjb3VudChwb3N0X2NvcnJlY3QpICU+JQogIHJlbmFtZSgic2hvb3RpbmdzX3NpbmNlXzIwMTgiID0gbikKCiMgam9pbiB0aGUgY291bnRzIHRvIHRoZSBwb3N0IHNoYXBlZmlsZQpwb3N0c0BkYXRhIDwtIHBvc3RzQGRhdGEgJT4lIAogIGxlZnRfam9pbigKICAgIHBvc3Rfc2hvb3RpbmdzXzIwMThfdG9fcHJlc2VudCwKICAgIGJ5ID0gYygiQXJlYSIgPSAicG9zdF9jb3JyZWN0IikKICApCgptaWNyb3pvbmVfc2hvb3RpbmdzXzIwMThfdG9fcHJlc2VudCA8LSBkZl9nZW9AZGF0YSAlPiUKICBmaWx0ZXIoeWVhcihjcmltZWRhdGUpID49IDIwMTgsCiAgICAgICAgIGRlc2NyaXB0aW9uID09ICJTSE9PVElORyIpICU+JQogIGNvdW50KEhTX1pvbmUpICU+JQogIHJlbmFtZSgic2hvb3RpbmdzX3NpbmNlXzIwMTgiID0gbikKCm1pY3Jvem9uZXNAZGF0YSA8LSBtaWNyb3pvbmVzQGRhdGEgJT4lCiAgbGVmdF9qb2luKAogICAgbWljcm96b25lX3Nob290aW5nc18yMDE4X3RvX3ByZXNlbnQsCiAgICBieSA9IGMoIkhTX1pvbmUiID0gIkhTX1pvbmUiKQogICkKYGBgCgpgYGB7ciByb2xsaW5nX2NvdW50c30KCiMgQnkgZGlzdHJpY3QKY291bnRzX2Rpc3RyaWN0cyA8LSBkZl9nZW9AZGF0YSAlPiUKICBjb3VudChkZXNjcmlwdGlvbiwgZGlzdHJpY3QsIGNyaW1lZGF0ZSkgJT4lCiAgZ3JvdXBfYnkoZGVzY3JpcHRpb24sIGRpc3RyaWN0KSAlPiUKICBjb21wbGV0ZShjcmltZWRhdGUgPSBzZXEuRGF0ZShhcy5EYXRlKG1pbihkZl9nZW9AZGF0YSRjcmltZWRhdGUpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuRGF0ZShtYXgoZGZfZ2VvQGRhdGEkY3JpbWVkYXRlKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnk9ImRheSIpKSAlPiUKICByZXBsYWNlX25hKGxpc3QobiA9IDApKSAlPiUKICB1bmdyb3VwKCkKCmhvbV9zaG90X2NvbWJvX2Rpc3RyaWN0X2NvdW50cyA8LSBjb3VudHNfZGlzdHJpY3RzICU+JSAKICBmaWx0ZXIoZGVzY3JpcHRpb24gJWluJSBjKCJTSE9PVElORyIsICJIT01JQ0lERSIpKSAlPiUgCiAgZ3JvdXBfYnkoZGlzdHJpY3QsIGNyaW1lZGF0ZSkgJT4lIAogIHNlbGVjdChkZXNjcmlwdGlvbiwgZGlzdHJpY3QsIGNyaW1lZGF0ZSwgbikgJT4lIAogIHNwcmVhZChrZXkgPSBkZXNjcmlwdGlvbiwgdmFsdWUgPSBuKSAlPiUgCiAgbXV0YXRlKG4gPSBIT01JQ0lERSArIFNIT09USU5HLCAKICAgICAgICAgZGVzY3JpcHRpb24gPSAiSE9NSUNJREUgKyBTSE9PVElORyIpICU+JSAKICBzZWxlY3QoLUhPTUlDSURFLCAtU0hPT1RJTkcpICU+JSAKICB1bmdyb3VwKCkKCnJvbGxpbmdfY291bnRzX2Rpc3RyaWN0cyA8LSBjb3VudHNfZGlzdHJpY3RzICU+JQogIGJpbmRfcm93cyhob21fc2hvdF9jb21ib19kaXN0cmljdF9jb3VudHMpICU+JQogIGFycmFuZ2UoY3JpbWVkYXRlKSAlPiUKICBncm91cF9ieShkaXN0cmljdCwgZGVzY3JpcHRpb24pICU+JQogIG11dGF0ZShyb2xsXzI4ID0gcm9sbF9zdW0oeCA9IG4sIG4gPSAyOCwgYWxpZ24gPSAicmlnaHQiLCBmaWxsID0gTkEpLAogICAgICAgICByb2xsXzcgPSByb2xsX3N1bSh4ID0gbiwgbiA9IDcsIGFsaWduID0gInJpZ2h0IiwgZmlsbCA9IE5BKSwKICAgICAgICAgcm9sbF85MCA9IHJvbGxfc3VtKHggPSBuLCBuID0gOTAsIGFsaWduID0gInJpZ2h0IiwgZmlsbCA9IE5BKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoZGVzY3JpcHRpb24sIGRpc3RyaWN0LCBjcmltZWRhdGUpCgojIENpdHl3aWRlCgpjb3VudHMgPC0gZGZfZ2VvQGRhdGEgJT4lCiAgY291bnQoZGVzY3JpcHRpb24sIGNyaW1lZGF0ZSkgJT4lCiAgZ3JvdXBfYnkoZGVzY3JpcHRpb24pICU+JQogIGNvbXBsZXRlKGNyaW1lZGF0ZSA9IHNlcS5EYXRlKGFzLkRhdGUobWluKGRmX2dlb0BkYXRhJGNyaW1lZGF0ZSkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5EYXRlKG1heChkZl9nZW9AZGF0YSRjcmltZWRhdGUpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieT0iZGF5IikpICU+JQogIHJlcGxhY2VfbmEobGlzdChuID0gMCkpICU+JQogIG11dGF0ZShkaXN0cmljdCA9ICJDSVRZV0lERSIpCgoKaG9tX3Nob3RfY29tYm9fY291bnRzIDwtIGNvdW50cyAlPiUgCiAgZmlsdGVyKGRlc2NyaXB0aW9uICVpbiUgYygiU0hPT1RJTkciLCAiSE9NSUNJREUiKSkgJT4lIAogIGdyb3VwX2J5KGNyaW1lZGF0ZSkgJT4lIAogIHNlbGVjdChkZXNjcmlwdGlvbiwgY3JpbWVkYXRlLCBuKSAlPiUgCiAgc3ByZWFkKGtleSA9IGRlc2NyaXB0aW9uLCB2YWx1ZSA9IG4pICU+JSAKICBtdXRhdGUobiA9IEhPTUlDSURFICsgU0hPT1RJTkcsIAogICAgICAgICBkZXNjcmlwdGlvbiA9ICJIT01JQ0lERSArIFNIT09USU5HIiwKICAgICAgICAgZGlzdHJpY3QgPSAiQ0lUWVdJREUiKSAlPiUgCiAgc2VsZWN0KC1IT01JQ0lERSwgLVNIT09USU5HKSAlPiUgCiAgdW5ncm91cCgpCgpyb2xsaW5nX2NvdW50cyA8LSBjb3VudHMgJT4lCiAgYmluZF9yb3dzKGhvbV9zaG90X2NvbWJvX2NvdW50cykgJT4lCiAgYXJyYW5nZShjcmltZWRhdGUpICU+JQogIGdyb3VwX2J5KGRlc2NyaXB0aW9uKSAlPiUKICBtdXRhdGUocm9sbF8yOCA9IHJvbGxfc3VtKHggPSBuLCBuID0gMjgsIGFsaWduID0gInJpZ2h0IiwgZmlsbCA9IE5BKSwKICAgICAgICAgcm9sbF83ID0gcm9sbF9zdW0oeCA9IG4sIG4gPSA3LCBhbGlnbiA9ICJyaWdodCIsIGZpbGwgPSBOQSksCiAgICAgICAgIHJvbGxfOTAgPSByb2xsX3N1bSh4ID0gbiwgbiA9IDkwLCBhbGlnbiA9ICJyaWdodCIsIGZpbGwgPSBOQSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKGRlc2NyaXB0aW9uLCBjcmltZWRhdGUpCgpgYGAKCgoKIyBDaXR5d2lkZQoKCmBgYHtyIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQoKIyBmb2xkZXIgPC0gcGFzdGUwKCIuLi9vdXRwdXQvcGxvdHMvIiwgbGFzdF9kYXRlLCAiLyIpCmZvbGRlciA8LSBwYXN0ZTAoIi4uLy4uLy4uLy4uLy4uL0dvb2dsZSBEcml2ZS9PZmZpY2Ugb2YgUGVyZm9ybWFuY2UgJiBJbm5vdmF0aW9uL0NpdGlTdGF0L1BvbGljZVN0YXQvUmVndWxhciBSZXBvcnRpbmcvIiwgbGFzdF9kYXRlLCAiLyIpCiAKaWYgKCFkaXIuZXhpc3RzKGZvbGRlcikpewogIGRpci5jcmVhdGUoZm9sZGVyKQp9CgoKZm9yKGRlc2MgaW4gdW5pcXVlKHJvbGxpbmdfY291bnRzJGRlc2NyaXB0aW9uKSl7CiAgCiAgZm5fcm9sbDkwIDwtIHBhc3RlMChmb2xkZXIsIGxhc3RfZGF0ZSwgIl9yb2xsaW5nXzkwX2RheV9jaXR5d2lkZV8iLCBkZXNjLCAiLnBuZyIpCiAgZm5fcm9sbDI4IDwtIHBhc3RlMChmb2xkZXIsIGxhc3RfZGF0ZSwgIl9yb2xsaW5nXzI4X2RheV9jaXR5d2lkZV8iLCBkZXNjLCAiLnBuZyIpCiAgCiAgcGx0X3JvbGwyOCA8LSByb2xsMjhfZGlzdHJpY3RfZmFjZXRfcGxvdChyb2xsaW5nX2NvdW50cywgZGVzYykKICBwbHRfcm9sbDkwIDwtIHJvbGw5MF9kaXN0cmljdF9mYWNldF9wbG90KHJvbGxpbmdfY291bnRzLCBkZXNjKQogIAogIGdnc2F2ZShmaWxlbmFtZSA9IGZuX3JvbGwyOCwgcGx0X3JvbGwyOCwgZGV2aWNlID0gInBuZyIsIAogICAgICAgICB3aWR0aCA9IDUsIGhlaWdodCA9IDMsIHVuaXRzID0gImluIikKICAKICBnZ3NhdmUoZmlsZW5hbWUgPSBmbl9yb2xsOTAsIHBsdF9yb2xsOTAsIGRldmljZSA9ICJwbmciLCAKICAgICAgICAgd2lkdGggPSA1LCBoZWlnaHQgPSAzLCB1bml0cyA9ICJpbiIpCiAgCiAgCiAgcHJpbnQocGx0X3JvbGwyOCkKICBwcmludChwbHRfcm9sbDkwKQp9CmBgYAoKYGBge3IgZmlnLmhlaWdodCA9IDIsIGZpZy53aWR0aCA9IDQsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQpyb2xsOTBfZGlzdHJpY3RfZmFjZXRfcGxvdChyb2xsaW5nX2NvdW50cywgIkhPTUlDSURFICsgU0hPT1RJTkciKSArCiAgZ2VvbV90ZXh0KGFlcyh4ID0gYXMuRGF0ZSgiMjAxOS0wNy0wMSIpIC0gMTQgLCB5ID0gMjUwLCBsYWJlbCA9ICJKdWx5IDEiKSwKICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgZmFtaWx5ID0gIm9zd2FsZCIsIGhqdXN0ID0gMSkgKwogIHRoZW1lX2l0ZWFtX3ByZXNlbnRhdGlvbnMoKQpgYGAKYGBge3IgZmlnLmhlaWdodCA9IC43NSwgZmlnLndpZHRoID0gMiwgd2FybmluZz1GLCBtZXNzYWdlPUZ9CnJvbGw5MF9kaXN0cmljdF9mYWNldF9wbG90X3NwYXJrbGluZShyb2xsaW5nX2NvdW50cywgIkhPTUlDSURFICsgU0hPT1RJTkciKSArCiAgIyBnZW9tX3RleHQoYWVzKHggPSBhcy5EYXRlKCIyMDE5LTA3LTAxIikgLSAxNCAsIHkgPSAyNTAsIGxhYmVsID0gIkp1bHkgMSIpLAogICMgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGZhbWlseSA9ICJvc3dhbGQiLCBoanVzdCA9IDEpICsKICAjdGhlbWVfaXRlYW1fcHJlc2VudGF0aW9ucygpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2xpbmUoc2l6ZT0uNSwgY29sb3I9ImdyYXk5MCIgKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpKQogICAgICAgIApgYGAKCiMgQnkgRGlzdHJpY3QKCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KCgoKZm9yKGRlc2MgaW4gdW5pcXVlKHJvbGxpbmdfY291bnRzX2Rpc3RyaWN0cyRkZXNjcmlwdGlvbikpewogIAogIGZuX3JvbGw5MCA8LSBwYXN0ZTAoZm9sZGVyLCBsYXN0X2RhdGUsICJfcm9sbGluZ185MF9kYXlfYnlfZGlzdHJpY3RfIiwgZGVzYywgIi5wbmciKQogIGZuX3JvbGwyOCA8LSBwYXN0ZTAoZm9sZGVyLCBsYXN0X2RhdGUsICJfcm9sbGluZ18yOF9kYXlfYnlfZGlzdHJpY3RfIiwgZGVzYywgIi5wbmciKQogIAogIHBsdF9yb2xsMjggPC0gcm9sbDI4X2Rpc3RyaWN0X2ZhY2V0X3Bsb3Qocm9sbGluZ19jb3VudHNfZGlzdHJpY3RzLCBkZXNjKQogIHBsdF9yb2xsOTAgPC0gcm9sbDkwX2Rpc3RyaWN0X2ZhY2V0X3Bsb3Qocm9sbGluZ19jb3VudHNfZGlzdHJpY3RzLCBkZXNjKQogIAogIGdnc2F2ZShmaWxlbmFtZSA9IGZuX3JvbGwyOCwgcGx0X3JvbGwyOCwgZGV2aWNlID0gInBuZyIsIAogICAgICAgICB3aWR0aCA9IDcsIGhlaWdodCA9IDksIHVuaXRzID0gImluIikKICAKICBnZ3NhdmUoZmlsZW5hbWUgPSBmbl9yb2xsOTAsIHBsdF9yb2xsOTAsIGRldmljZSA9ICJwbmciLCAKICAgICAgICAgd2lkdGggPSA3LCBoZWlnaHQgPSA5LCB1bml0cyA9ICJpbiIpCiAgCiAgCiAgcHJpbnQocGx0X3JvbGwyOCkKICBwcmludChwbHRfcm9sbDkwKQp9Cgojb3V0cHV0LnBuZyA8LSBwYXN0ZTAoZm9sZGVyLCBsYXN0X2RhdGUsICJfcm9sbGluZ18yOF9kYXlfYnlfZGlzdHJpY3QucG5nIikKI3N0YXBsZV9wbmcoaW5wdXRfZGlyZWN0b3J5ID0gZm9sZGVyLCBvdXRwdXRfZmlsZXBhdGggPSAib3V0cHV0LnBuZyIpIG5vdCB3b3JraW5nCmBgYAoKYGBge3IgZmlnLndpZHRoID0gMywgZmlnLmhlaWdodCA9IDJ9CnJvbGw5MF9zaW5nbGVfZGlzdHJpY3RfcGxvdCgiSE9NSUNJREUgKyBTSE9PVElORyIsICkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBhcy5EYXRlKCIyMDE5LTA3LTAxIikpLCBjb2xvciA9ICJyZWQiLCBzaXplID0gLjUsIGxpbmV0eXBlID0gImRvdHRlZCIpICsKICB0aGVtZV9pdGVhbV9wcmVzZW50YXRpb25zKCkKCmBgYAoKYGBge3IgZmlnLndpZHRoID0gMywgZmlnLmhlaWdodD0xLjV9CmhvbV9zaG90X2N1bXN1bXMgPC0gZGZfZ2VvQGRhdGEgJT4lCiAgZmlsdGVyKGRlc2NyaXB0aW9uICVpbiUgYygiSE9NSUNJREUiLCAiU0hPT1RJTkciKSkgJT4lCiAgY291bnQoY3JpbWVkYXRlKSAlPiUKICBjb21wbGV0ZShjcmltZWRhdGUgPSBzZXEuRGF0ZShhcy5EYXRlKG1pbihkZl9nZW9AZGF0YSRjcmltZWRhdGUpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuRGF0ZShtYXgoZGZfZ2VvQGRhdGEkY3JpbWVkYXRlKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnk9ImRheSIpKSAlPiUKICByZXBsYWNlX25hKGxpc3QobiA9IDApKSAlPiUKICBhcnJhbmdlKGNyaW1lZGF0ZSkgJT4lCiAgbXV0YXRlKGRheV9vZl95ZWFyID0gYXMubnVtZXJpYyhzdHJmdGltZShjcmltZWRhdGUsICIlaiIpKSwKICAgICAgICAgY3JpbWUueWVhciA9IHllYXIoY3JpbWVkYXRlKSkgJT4lCiAgZ3JvdXBfYnkoY3JpbWUueWVhcikgJT4lCiAgbXV0YXRlKGhzX2N1bXN1bSA9IGN1bXN1bShuKSkKCmhvbV9zaG90X2N1cnJlbnQgPC0gaG9tX3Nob3RfY3Vtc3VtcyAlPiUKICBmaWx0ZXIoY3JpbWUueWVhciA9PSAyMDE5KSAlPiUKICBzdW1tYXJpc2UobWF4KGhzX2N1bXN1bSkpICU+JQogIHB1bGwoKQoKY3VycmVudF9kYXkgPC0gaG9tX3Nob3RfY3Vtc3VtcyAlPiUKICBmaWx0ZXIoIWlzLm5hKGhzX2N1bXN1bSksCiAgICAgICAgIGNyaW1lLnllYXIgPT0gMjAxOSkgJT4lCiAgdW5ncm91cCgpICU+JQogIHN1bW1hcmlzZShtYXgoZGF5X29mX3llYXIpKSAlPiUKICBwdWxsKCkKCmhvbV9zaG90X3Byb2plY3Rpb25zIDwtIHJvdW5kKDM2NSAqIGhvbV9zaG90X2N1cnJlbnQgLyBjdXJyZW50X2RheSwgMCkKCmN1bS5wbG90IDwtIGhvbV9zaG90X2N1bXN1bXMgJT4lCiAgZ2dwbG90KGFlcyhkYXlfb2ZfeWVhciwgaHNfY3Vtc3VtLCAKICAgICAgICAgICAgIGdyb3VwID0gY3JpbWUueWVhciwgCiAgICAgICAgICAgICBjb2xvciA9IGFzLmZhY3RvcihjcmltZS55ZWFyKSkpICsKICBnZW9tX2xpbmUoKSArCiAgI3NjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXMgPSBhcy5mYWN0b3IoYygxLCAxLCAwLjMsIDEuLCAxKSkpICsKICB0aGVtZV9pdGVhbV9wcmVzZW50YXRpb25zKCkgKwogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlX2l0ZWFtKCkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHlsYWIoIkN1bXVsYXRpdmVcbkhvbS4gKyBTaG90cy4iKSArCiAgeGxhYigiRGF5IG9mIFllYXIiKSAKCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChmb2xkZXIsIGxhc3RfZGF0ZSwgImN1bXVsYXRpdmVfaG9tX3Nob290LnBuZyIpLCBjdW0ucGxvdCwgZGV2aWNlID0gInBuZyIsIAogICAgICAgd2lkdGggPSA0LCBoZWlnaHQgPSAyLCB1bml0cyA9ICJpbiIpCgpjdW0ucGxvdApgYGAKCmBgYHtyIGZpZy53aWR0aCA9IDMsIGZpZy5oZWlnaHQ9MS41fQpob21fY3Vtc3VtcyA8LSBkZl9nZW9AZGF0YSAlPiUKICBmaWx0ZXIoZGVzY3JpcHRpb24gJWluJSBjKCJIT01JQ0lERSIpKSAlPiUKICBjb3VudChjcmltZWRhdGUpICU+JQogIGNvbXBsZXRlKGNyaW1lZGF0ZSA9IHNlcS5EYXRlKGFzLkRhdGUobWluKGRmX2dlb0BkYXRhJGNyaW1lZGF0ZSkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5EYXRlKG1heChkZl9nZW9AZGF0YSRjcmltZWRhdGUpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieT0iZGF5IikpICU+JQogIHJlcGxhY2VfbmEobGlzdChuID0gMCkpICU+JQogIGFycmFuZ2UoY3JpbWVkYXRlKSAlPiUKICBtdXRhdGUoZGF5X29mX3llYXIgPSBhcy5udW1lcmljKHN0cmZ0aW1lKGNyaW1lZGF0ZSwgIiVqIikpLAogICAgICAgICBjcmltZS55ZWFyID0geWVhcihjcmltZWRhdGUpKSAlPiUKICBncm91cF9ieShjcmltZS55ZWFyKSAlPiUKICBtdXRhdGUoaG9tX2N1bXN1bSA9IGN1bXN1bShuKSkKCmhvbV9jdXJyZW50IDwtIGhvbV9jdW1zdW1zICU+JQogIGZpbHRlcihjcmltZS55ZWFyID09IDIwMTkpICU+JQogIHN1bW1hcmlzZShtYXgoaG9tX2N1bXN1bSkpICU+JQogIHB1bGwoKQoKaG9tX3Byb2plY3Rpb25zIDwtIHJvdW5kKDM2NSAqIGhvbV9jdXJyZW50IC8gY3VycmVudF9kYXksIDApCgpjdW0ucGxvdCA8LSBob21fY3Vtc3VtcyAlPiUKICBnZ3Bsb3QoYWVzKGRheV9vZl95ZWFyLCBob21fY3Vtc3VtLCAKICAgICAgICAgICAgIGdyb3VwID0gY3JpbWUueWVhciwgCiAgICAgICAgICAgICBjb2xvciA9IGFzLmZhY3RvcihjcmltZS55ZWFyKSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGhvbV9jdW1zdW1zICU+JSAKICAgICAgICAgICAgICAgICAgIGZpbHRlcihjcmltZS55ZWFyID09IDIwMTkpICU+JQogICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKG1heChkYXlfb2ZfeWVhcikpICU+JQogICAgICAgICAgICAgICAgICAgcHVsbCgpLCAKICAgICAgICAgICAgICAgICB5ID0gaG9tX2N1bXN1bXMgJT4lIAogICAgICAgICAgICAgICAgICAgZmlsdGVyKGNyaW1lLnllYXIgPT0gMjAxOSkgJT4lCiAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UobWF4KGhvbV9jdW1zdW0pKSAlPiUKICAgICAgICAgICAgICAgICAgIHB1bGwoKSksCiAgICAgICAgICAgICBjb2xvciA9IGl0ZWFtLmNvbG9yc1szXSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSAzNjUsIAogICAgICAgICAgICAgICAgIHkgPSBob21fY3Vtc3VtcyAlPiUgCiAgICAgICAgICAgICAgICAgICBmaWx0ZXIoY3JpbWUueWVhciA9PSAyMDE4KSAlPiUKICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShtYXgoaG9tX2N1bXN1bSkpICU+JQogICAgICAgICAgICAgICAgICAgcHVsbCgpKSwKICAgICAgICAgICAgIGNvbG9yID0gaXRlYW0uY29sb3JzWzJdKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IDM2NSwgCiAgICAgICAgICAgICAgICAgeSA9IGhvbV9jdW1zdW1zICU+JSAKICAgICAgICAgICAgICAgICAgIGZpbHRlcihjcmltZS55ZWFyID09IDIwMTcpICU+JQogICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKG1heChob21fY3Vtc3VtKSkgJT4lCiAgICAgICAgICAgICAgICAgICBwdWxsKCkpLAogICAgICAgICAgICAgY29sb3IgPSBpdGVhbS5jb2xvcnNbMV0pICsKICBnZW9tX3BvaW50KGFlcyh4ID0gMzY1LCB5ID0gaG9tX3Byb2plY3Rpb25zKSwgY29sb3IgPSBpdGVhbS5jb2xvcnNbM10pICsKICAKICBnZW9tX3RleHQoYWVzKHggPSBob21fY3Vtc3VtcyAlPiUgCiAgICAgICAgICAgICAgICAgIGZpbHRlcihjcmltZS55ZWFyID09IDIwMTkpICU+JQogICAgICAgICAgICAgICAgICBzdW1tYXJpc2UobWF4KGRheV9vZl95ZWFyKSkgJT4lCiAgICAgICAgICAgICAgICAgIHB1bGwoKSwgCiAgICAgICAgICAgICAgICB5ID0gaG9tX2N1bXN1bXMgJT4lIAogICAgICAgICAgICAgICAgICBmaWx0ZXIoY3JpbWUueWVhciA9PSAyMDE5KSAlPiUKICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKG1heChob21fY3Vtc3VtKSkgJT4lCiAgICAgICAgICAgICAgICAgIHB1bGwoKSArMTAsCiAgICAgICAgICAgICAgICBsYWJlbCA9IGhvbV9jdW1zdW1zICU+JSAKICAgICAgICAgICAgICAgICAgZmlsdGVyKGNyaW1lLnllYXIgPT0gMjAxOSkgJT4lCiAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShtYXgoaG9tX2N1bXN1bSkpICU+JQogICAgICAgICAgICAgICAgICBwdWxsKCksCiAgICAgICAgICAgICAgICBoanVzdCA9IDEpLAogICAgICAgICAgICBjb2xvciA9IGl0ZWFtLmNvbG9yc1szXSkgKwogIGdlb21fdGV4dChhZXMoeCA9IDM3NSwgCiAgICAgICAgICAgICAgICB5ID0gaG9tX2N1bXN1bXMgJT4lIAogICAgICAgICAgICAgICAgICBmaWx0ZXIoY3JpbWUueWVhciA9PSAyMDE4KSAlPiUKICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKG1heChob21fY3Vtc3VtKSkgJT4lCiAgICAgICAgICAgICAgICAgIHB1bGwoKSwKICAgICAgICAgICAgICAgIGxhYmVsID0gaG9tX2N1bXN1bXMgJT4lIAogICAgICAgICAgICAgICAgICBmaWx0ZXIoY3JpbWUueWVhciA9PSAyMDE4KSAlPiUKICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKG1heChob21fY3Vtc3VtKSkgJT4lCiAgICAgICAgICAgICAgICAgIHB1bGwoKSwKICAgICAgICAgICAgICAgIGhqdXN0ID0gMCksCiAgICAgICAgICAgIGNvbG9yID0gaXRlYW0uY29sb3JzWzJdKSArCiAgZ2VvbV90ZXh0KGFlcyh4ID0gMzc1LCAKICAgICAgICAgICAgICAgIHkgPSBob21fY3Vtc3VtcyAlPiUgCiAgICAgICAgICAgICAgICAgIGZpbHRlcihjcmltZS55ZWFyID09IDIwMTcpICU+JQogICAgICAgICAgICAgICAgICBzdW1tYXJpc2UobWF4KGhvbV9jdW1zdW0pKSAlPiUKICAgICAgICAgICAgICAgICAgcHVsbCgpIC0gMTAsCiAgICAgICAgICAgICAgICBsYWJlbCA9IGhvbV9jdW1zdW1zICU+JSAKICAgICAgICAgICAgICAgICAgZmlsdGVyKGNyaW1lLnllYXIgPT0gMjAxNykgJT4lCiAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShtYXgoaG9tX2N1bXN1bSkpICU+JQogICAgICAgICAgICAgICAgICBwdWxsKCksCiAgICAgICAgICAgICAgICBoanVzdCA9IDApLAogICAgICAgICAgICBjb2xvciA9IGl0ZWFtLmNvbG9yc1sxXSkgKwogIGdlb21fdGV4dChhZXMoeCA9IDM3NSwgeSA9IGhvbV9wcm9qZWN0aW9ucyArIDEwLAogICAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZShob21fcHJvamVjdGlvbnMsICIocHJvai4pIiksCiAgICAgICAgICAgICAgICBoanVzdCA9IDApLCAKICAgICAgICAgICAgICAgIGNvbG9yID0gaXRlYW0uY29sb3JzWzNdKSArCiAgI3NjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXMgPSBhcy5mYWN0b3IoYygxLCAxLCAwLjMsIDEuLCAxKSkpICsKICB0aGVtZV9pdGVhbV9wcmVzZW50YXRpb25zKCkgKwogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlX2l0ZWFtKCkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKDAsIDEwMCwgMjAwLCAzMDAsIDM2NSksCiAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCwgNDUwKSkgKwogIHlsYWIoIkN1bXVsYXRpdmUgSG9taWNpZGVzIikgKwogIHhsYWIoIkRheSBvZiBZZWFyIikgCgojZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKGZvbGRlciwgbGFzdF9kYXRlLCAiY3VtdWxhdGl2ZV9ob21fc2hvb3QucG5nIiksIGN1bS5wbG90LCBkZXZpY2UgPSAicG5nIiwgCiMgICAgICAgd2lkdGggPSA0LCBoZWlnaHQgPSAyLCB1bml0cyA9ICJpbiIpCgpjdW0ucGxvdApgYGAKCmBgYHtyIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQ9Mn0KZGYgJT4lCiAgZmlsdGVyKGRlc2NyaXB0aW9uICVpbiUgYygiSE9NSUNJREUiLCAiU0hPT1RJTkciKSwKICAgICAgICAgeWVhcihjcmltZWRhdGUpICVpbiUgYygyMDE4LCAyMDE5KSkgJT4lCiAgY291bnQoY3JpbWVkYXRlKSAlPiUKICBncm91cF9ieSh5ZWFyKGNyaW1lZGF0ZSkpICU+JQogIGFycmFuZ2UoY3JpbWVkYXRlKSAlPiUKICBtdXRhdGUoZGF5X29mX3llYXIgPSByb3dfbnVtYmVyKCksCiAgICAgICAgIGhzX2N1bXN1bSA9IGN1bXN1bShuKSkgJT4lCiAgZmlsdGVyKG1vbnRoKGNyaW1lZGF0ZSkgPT0gOCkKYGBgCmBgYHtyIGZpZy53aWR0aCA9IDMsIGZpZy5oZWlnaHQ9MS41fQpjdW0ucGxvdCA8LSBkZl9nZW9AZGF0YSAlPiUKICBmaWx0ZXIoZGVzY3JpcHRpb24gJWluJSBjKCJIT01JQ0lERSIsICJTSE9PVElORyIpLAogICAgICAgICB5ZWFyKGNyaW1lZGF0ZSkgPT0gMjAxOSkgJT4lCiAgY29tcGxldGUoY3JpbWVkYXRlID0gc2VxLkRhdGUoYXMuRGF0ZShtaW4oZGZfZ2VvQGRhdGEkY3JpbWVkYXRlKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLkRhdGUobWF4KGRmX2dlb0BkYXRhJGNyaW1lZGF0ZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5PSJkYXkiKSkgJT4lCiAgY291bnQoZGVzY3JpcHRpb24sIGNyaW1lZGF0ZSkgJT4lCiAgcmVwbGFjZV9uYShsaXN0KG4gPSAwKSkgJT4lCiAgYXJyYW5nZShjcmltZWRhdGUpICU+JQogIG11dGF0ZShkYXlfb2ZfeWVhciA9IGFzLm51bWVyaWMoc3RyZnRpbWUoY3JpbWVkYXRlLCAiJWoiKSkpICU+JQogIGdyb3VwX2J5KGRlc2NyaXB0aW9uKSAlPiUKICBtdXRhdGUoaHNfY3Vtc3VtID0gY3Vtc3VtKG4pLAogICAgICAgICBoc19jdW1wY3QgPSBoc19jdW1zdW0gLyBzdW0obikpICU+JQogIGdncGxvdChhZXMoZGF5X29mX3llYXIsIGhzX2N1bXBjdCwgCiAgICAgICAgICAgICBncm91cCA9IGRlc2NyaXB0aW9uLCAKICAgICAgICAgICAgIGNvbG9yID0gYXMuZmFjdG9yKGRlc2NyaXB0aW9uKSkpICsKICBnZW9tX2xpbmUoKSArCiAgI3NjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXMgPSBhcy5mYWN0b3IoYygxLCAxLCAwLjMsIDEuLCAxKSkpICsKICB0aGVtZV9pdGVhbV9wcmVzZW50YXRpb25zKCkgKwogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlX2l0ZWFtKCkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHlsYWIoIkN1bS4gSG9tLiArIFNob3RzLiIpICsKICB4bGFiKCJEYXkgb2YgWWVhciIpICsKICB4bGltKGMoMCwyNTApKQoKI2dnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChmb2xkZXIsIGxhc3RfZGF0ZSwgImN1bXVsYXRpdmVfaG9tX3Nob290LnBuZyIpLCBjdW0ucGxvdCwgZGV2aWNlID0gInBuZyIsIAojICAgICAgIHdpZHRoID0gNCwgaGVpZ2h0ID0gMiwgdW5pdHMgPSAiaW4iKQoKY3VtLnBsb3QKYGBgCgpgYGB7ciBmaWcud2lkdGggPSAzLCBmaWcuaGVpZ2h0PTEuNX0KY291bnRzICU+JQogIGZpbHRlcihkZXNjcmlwdGlvbiAlaW4lIGMoIkhPTUlDSURFIiwgIlNIT09USU5HIiksCiAgICAgICAgIHllYXIoY3JpbWVkYXRlKSA9PSAyMDE5KSAlPiUKICBncm91cF9ieShkZXNjcmlwdGlvbikgJT4lCiAgbXV0YXRlKGhzX2N1bXN1bSA9IGN1bXN1bShuKSwKICAgICAgICAgZGF5X29mX3llYXIgPSBhcy5udW1lcmljKHN0cmZ0aW1lKGNyaW1lZGF0ZSwgIiVqIikpLAogICAgICAgICBkYXkuYXZnID0gaHNfY3Vtc3VtIC8gZGF5X29mX3llYXIpICU+JQogIGdncGxvdChhZXMoZGF5X29mX3llYXIsIGRheS5hdmcpKSArCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IGRlc2NyaXB0aW9uKSkgCgpgYGAKYGBge3IgZmlnLndpZHRoID0gMywgZmlnLmhlaWdodD0xLjV9CmNvdW50cyAlPiUKICBmaWx0ZXIoZGVzY3JpcHRpb24gJWluJSBjKCJIT01JQ0lERSIsICJTSE9PVElORyIpLAogICAgICAgICB5ZWFyKGNyaW1lZGF0ZSkgPT0gMjAxOSkgJT4lCiAgZ3JvdXBfYnkoZGVzY3JpcHRpb24pICU+JQogIG11dGF0ZShoc19jdW1zdW0gPSBjdW1zdW0obiksCiAgICAgICAgIGRheV9vZl95ZWFyID0gYXMubnVtZXJpYyhzdHJmdGltZShjcmltZWRhdGUsICIlaiIpKSwKICAgICAgICAgZGF5LmF2ZyA9IGhzX2N1bXN1bSAvIGRheV9vZl95ZWFyKSAlPiUKICBzZWxlY3QoLWNyaW1lZGF0ZSwgLW4sIC1kYXkuYXZnLCAtIGRpc3RyaWN0KSAlPiUKICBzcHJlYWQoZGVzY3JpcHRpb24sIGhzX2N1bXN1bSkgJT4lCiAgbXV0YXRlKGZhdGFsaXR5ID0gSE9NSUNJREUgLyAoSE9NSUNJREUgKyBTSE9PVElORykpICU+JQogIGdncGxvdChhZXMoZGF5X29mX3llYXIsIGZhdGFsaXR5KSkgKyAKICBnZW9tX2xpbmUoKQoKYGBgCgpgYGB7ciBmaWcud2lkdGggPSAxLjUsIGZpZy5oZWlnaHQgPSAxLjV9CnJvbGw5MF9zaW5nbGVfZGlzdHJpY3RfcGxvdCgiUk9CQkVSWSIsICJOT1JUSEVBU1QiKQoKYGBgCgoKYGBge3IgZmlnLndpZHRoID0gMywgZmlnLmhlaWdodD0xLjV9CmRlY190aGVfaW5jIDwtIGRmX2dlb0BkYXRhICU+JQogIGZpbHRlcihkZXNjcmlwdGlvbl9ncm91cGVkID09ICJIT01JQ0lERS9TSE9PVElORyIpICU+JQogIGNvdW50KGNyaW1lZGF0ZSkgJT4lCiAgY29tcGxldGUoY3JpbWVkYXRlID0gc2VxLkRhdGUoYXMuRGF0ZShtaW4oZGZfZ2VvQGRhdGEkY3JpbWVkYXRlKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLkRhdGUobWF4KGRmX2dlb0BkYXRhJGNyaW1lZGF0ZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5PSJkYXkiKSkgJT4lCiAgcmVwbGFjZV9uYShsaXN0KG4gPSAwKSkgJT4lCiAgYXJyYW5nZShjcmltZWRhdGUpICU+JQogIG11dGF0ZShkYXlfb2ZfeWVhciA9IGFzLm51bWVyaWMoc3RyZnRpbWUoY3JpbWVkYXRlLCAiJWoiKSksCiAgICAgICAgIGNyaW1lLnllYXIgPSB5ZWFyKGNyaW1lZGF0ZSkpICU+JQogIGZpbHRlcihjcmltZS55ZWFyICVpbiUgYygyMDE4LCAyMDE5KSkgJT4lCiAgZ3JvdXBfYnkoY3JpbWUueWVhcikgJT4lCiAgbXV0YXRlKGhzX2N1bXN1bSA9IGN1bXN1bShuKSkgJT4lCiAgc2VsZWN0KC1uLCAtY3JpbWVkYXRlKSAlPiUKICBzcHJlYWQoa2V5ID0gY3JpbWUueWVhciwgdmFsdWUgPSBoc19jdW1zdW0pICU+JQogIG11dGF0ZShwY3RfY2hhbmdlID0gKGAyMDE5YCAtIGAyMDE4YCkgLyBgMjAxOGApCiAgCmRlY190aGVfaW5jICU+JQogIGdncGxvdChhZXMoZGF5X29mX3llYXIsIHBjdF9jaGFuZ2UpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHN1YnNldChkZWNfdGhlX2luYywgZGF5X29mX3llYXIgPT0geWRheShsYXN0X2RhdGUpKSwgCiAgICAgICAgICAgICBhZXMoeCA9IGRheV9vZl95ZWFyLCB5ID0gcGN0X2NoYW5nZSksCiAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBzaXplID0yKSArCiAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGRlY190aGVfaW5jLCBkYXlfb2ZfeWVhciA9PSB5ZGF5KGxhc3RfZGF0ZSkgLSAyOCksIAogICAgICAgICAgICAgYWVzKHggPSBkYXlfb2ZfeWVhciwgeSA9IHBjdF9jaGFuZ2UpLAogICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgc2l6ZSA9MikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGRlY190aGVfaW5jLCBkYXlfb2ZfeWVhciA9PSB5ZGF5KCIyMDE5LTA3LTAxIikpLCAKICAgICAgICAgICAgIGFlcyh4ID0gZGF5X29mX3llYXIsIHkgPSBwY3RfY2hhbmdlKSwKICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIHNpemUgPTIpICsKICB0aGVtZV9pdGVhbV9wcmVzZW50YXRpb25zKCkgKwogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlX2l0ZWFtKCkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHlsYWIoIiUgQ2hhbmdlIGZyb20gMjAxOCIpICsKICB4bGFiKCJEYXkgb2YgWWVhciIpICArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLS41LCAuNSksIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgCiAgCiMgZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKGZvbGRlciwgbGFzdF9kYXRlLCAiY3VtdWxhdGl2ZV9ob21fc2hvb3QucG5nIiksIGN1bS5wbG90LCBkZXZpY2UgPSAicG5nIiwgCiMgICAgICAgIHdpZHRoID0gNCwgaGVpZ2h0ID0gMiwgdW5pdHMgPSAiaW4iKQoKZGVjX3RoZV9pbmMKYGBgCgoKYGBge3J9CmRmX2dlb0BkYXRhICU+JQogIGNvdW50KHllYXIoY3JpbWVkYXRlKSwgZGVzY3JpcHRpb24pCgpgYGAKYGBge3IgZmlnLndpZHRoID0gMywgZmlnLmhlaWdodD0yfQpyb2xsOTBfc2luZ2xlX2Rpc3RyaWN0X3Bsb3QoIkhPTUlDSURFICsgU0hPT1RJTkciLCAiU09VVEhXRVNUIikKYGBgCgo=